package uk.ac.bris.cs.scotlandyard.model;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import uk.ac.bris.cs.scotlandyard.model.Board.GameState;
import uk.ac.bris.cs.scotlandyard.model.Model.Observer.Event;
import uk.ac.bris.cs.scotlandyard.model.ScotlandYard.Factory;

import javax.annotation.Nonnull;
import java.util.HashSet;
import java.util.Set;

/**
 * cw-model
 * Stage 2: Complete this class
 */
public final class MyModelFactory implements Factory<Model> {
  private static final class MyModel implements Model {
    private GameState gameState;
    private final Set<Observer> observers;

    private MyModel(GameState gameState) {
      this.observers = new HashSet<>();
      this.gameState = gameState;
    }

    @Override
    @Nonnull
    public Board getCurrentBoard() {
      return this.gameState;
    }

    @Override
    public void registerObserver(@Nonnull Observer observer) {
      if (observer == null) {
        throw new NullPointerException("register null observer");
      }
      if (this.observers.contains(observer)) {
        throw new IllegalArgumentException("More than Once");
      }
      this.observers.add(observer);
    }

    @Override
    public void unregisterObserver(@Nonnull Observer observer) {
      if (observer == null) {
        throw new NullPointerException("unregister null observer");
      }
      if (!observers.iterator().hasNext()) {
        throw new IllegalArgumentException("unregistered observer previously");
      }
      this.observers.remove(observer);
    }

    @Nonnull
    @Override
    public ImmutableSet<Observer> getObservers() {
      return ImmutableSet.copyOf(this.observers);
    }

    @Override
    public void chooseMove(@Nonnull Move move) {
      gameState = gameState.advance(move);

      if (gameState.getWinner().isEmpty()) {
        int i = 0;
        while (i < observers.size()) {
          observers.iterator().next().onModelChanged(gameState, Event.MOVE_MADE);
          ++i;
        }
      } else {
        int i = 0;
        do {
          observers.iterator().next().onModelChanged(gameState, Event.GAME_OVER);
          ++i;
        } while (i < observers.size());
      }
    }
  }

  @Nonnull
  @Override
  public Model build(GameSetup setup,
                     Player mrX,
                     ImmutableList<Player> detectives) {
    return new MyModel(new MyGameStateFactory().build(setup, mrX, detectives));
  }
}
